#include "napi/native_api.h"
#include <map>
#include <json/json.h>
#include <hilog/log.h>
#include <hiappevent/hiappevent.h>

#define NAPI_BINDER(NAME, FUNC) { NAME, nullptr, FUNC, nullptr, nullptr, nullptr, napi_default, nullptr}

namespace {
    static napi_value crash_npe(napi_env env, napi_callback_info info)
    {
        int *pArr = nullptr;
        int val = *pArr;
        napi_value result;
        napi_create_int32(env, val, &result);
        return result;
    }
    
    static napi_value crash_runtime(napi_env env, napi_callback_info info)
    {
        throw new std::runtime_error("Native crash runtime exception.");
    }
}

namespace {

const int MAX_LEN = 256;

// 订阅事件：OnReceive类型观察者、OnTrigger类型观察者的订阅方式
// OnReceive 观察者对象指针
static std::map<std::string, HiAppEvent_Watcher *> _onReceiverWatcherMap;
// onTrigger 观察者对象指针
static std::map<std::string, HiAppEvent_Watcher *> _onTriggerWatcherMap;

// 获取app event名称
static std::string getAppEventFromEnv(napi_env env, napi_callback_info info) {
    std::string result;
    // 获取第一个参数，第一个参数是 event name
    size_t argc = 1;
    napi_value argv[1];

    napi_status status;
    status = napi_get_cb_info(env, info, &argc, argv , nullptr, nullptr);
    if (status != napi_ok || argc != 1) {
        OH_LOG_ERROR(LogType::LOG_APP, "HiAppEvent: Parameter count is not 1.");
        return "";
    }

    napi_valuetype arg_type;
    napi_typeof(env, argv[0], &arg_type);
    if (arg_type != napi_valuetype::napi_string) {
        OH_LOG_ERROR(LogType::LOG_APP, "HiAppEvent: Parameter type is not string.");
        return "";
    }

    // event name 长度不超过 255
    
    char name[MAX_LEN] = {0};
    size_t copied_len;
    status = napi_get_value_string_utf8(env, argv[0], &name[0], MAX_LEN, &copied_len);
    if (status != napi_ok) {
        OH_LOG_ERROR(LogType::LOG_APP, "HiAppEvent: Failed to copy string.");
        return "";
    }
    result = std::string(name);
    OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent: app event type = %{public}s", result.c_str());
    return result;
}

// onReceiver 事件信息回调
static void OnReceive(const char *domain, const struct HiAppEvent_AppEventGroup *appEventGroups, uint32_t groupLen) {
    for (int i = 0; i < groupLen; ++i) {
        for (int j = 0; j < appEventGroups[i].infoLen; ++j) {
            OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.domain=%{public}s", appEventGroups[i].appEventInfos[j].domain);
            OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.name=%{public}s", appEventGroups[i].appEventInfos[j].name);
            OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.eventType=%{public}d", appEventGroups[i].appEventInfos[j].type);
            if (strcmp(appEventGroups[i].appEventInfos[j].domain, DOMAIN_OS) == 0 &&
                strcmp(appEventGroups[i].appEventInfos[j].name, EVENT_APP_CRASH) == 0) {
                Json::Value params;
                Json::Reader reader(Json::Features::strictMode());
                Json::FastWriter writer;
                if (reader.parse(appEventGroups[i].appEventInfos[j].params, params)) {
                    // 开发者可以获取到崩溃事件发生的时间戳
                    OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.time=%{public}ld", params["time"].asInt64());
                    // 开发者可以获取到崩溃应用的包名
                    OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_name=%{public}s", params["bundle_name"].asString().c_str());
                    auto external_log = writer.write(params["external_log"]);
                    // 开发者可以获取到崩溃事件发生时的故障日志文件
                    OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.external_log=%{public}s", external_log.c_str());
                }
            }
        }
    }
}

// onTrigger 事件信息回调
static void onTakeCallback(const char *const *events, uint32_t eventLen) {
    Json::Reader reader(Json::Features::strictMode());
    Json::FastWriter writer;
    for (int i = 0; i < eventLen; ++i) {
        Json::Value eventInfo;
        if (reader.parse(events[i], eventInfo)) {
            auto domain =  eventInfo["domain_"].asString();
            auto name = eventInfo["name_"].asString();
            auto type = eventInfo["type_"].asInt();
            OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.domain=%{public}s", domain.c_str());
            OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.name=%{public}s", name.c_str());
            OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.eventType=%{public}d", type);
            if (domain ==  DOMAIN_OS && name == EVENT_APP_CRASH) {
                auto time = eventInfo["time"].asInt64();
                auto crashType = eventInfo["crash_type"].asString();
                auto foreground = eventInfo["foreground"].asBool();
                auto bundleVersion = eventInfo["bundle_version"].asString();
                auto bundleName = eventInfo["bundle_name"].asString();
                auto pid = eventInfo["pid"].asInt();
                auto uid = eventInfo["uid"].asInt();
                auto uuid = eventInfo["uuid"].asString();
                auto exception = writer.write(eventInfo["exception"]);
                auto hilogSize = eventInfo["hilog"].size();
                auto externalLog = writer.write(eventInfo["external_log"]);
                auto logOverLimit = eventInfo["log_over_limit"].asBool();
                OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.time=%{public}ld", time);
                OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.crash_type=%{public}s", crashType.c_str());
                OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.foreground=%{public}d", foreground);
                OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_version=%{public}s", bundleVersion.c_str());
                OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_name=%{public}s", bundleName.c_str());
                OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.pid=%{public}d", pid);
                OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uid=%{public}d", uid);
                OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uuid=%{public}s", uuid.c_str());
                OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.exception=%{public}s", exception.c_str());
                OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.hilog.size=%{public}d", hilogSize);
                OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.external_log=%{public}s", externalLog.c_str());
                OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.log_over_limit=%{public}d", logOverLimit);
            }
        }
    }
}

// 开发者应实现订阅回调函数，用以对获取的事件打点数据进行自定义处理。
static void onTriggerCallback(int row, int size) {
    // 接收回调后，获取指定数量的事件。
    OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent: onTriggerCallback row = %{public}d, size = %{public}d", row, size);
}

// 订阅系统崩溃事件，OnReceiver类型观察者
// 可参考官网：https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/hiappevent-watcher-app-events-ndk
static napi_value addOnReceiverWatcher(napi_env env, napi_callback_info info) {
    // 获得 event name
    std::string event_name = getAppEventFromEnv(env, info);
    std::string event_name_tag = "Native-OnReceiver-Watcher-" + event_name;
    OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent: add watch event name = %{public}s", event_name.c_str());
    // 开发者自定义观察者名称，系统根据不同的名称来识别不同的观察者。
    HiAppEvent_Watcher *onReceiverWatcherPtr = nullptr;
    if (_onReceiverWatcherMap.count(event_name_tag) == 0) {
        onReceiverWatcherPtr = OH_HiAppEvent_CreateWatcher(event_name_tag.c_str());
        if (onReceiverWatcherPtr == nullptr) {
            OH_LOG_ERROR(LogType::LOG_APP, "HiAppEvent: Failed to add watcher %{public}s", event_name_tag.c_str());
            napi_value undefined;
            napi_get_undefined(env, &undefined);
            return undefined;
        }
        _onReceiverWatcherMap[event_name_tag] = onReceiverWatcherPtr;
        // 设置订阅的事件名称。
        const char *names[] = { event_name_tag.c_str() };
        // 开发者订阅感兴趣的事件，此处订阅了系统事件。
        OH_HiAppEvent_SetAppEventFilter(onReceiverWatcherPtr, DOMAIN_OS, 0, names, 1);
        // 开发者设置已实现的回调函数，观察者接收到事件后回立即触发OnReceive回调。
        OH_HiAppEvent_SetWatcherOnReceive(onReceiverWatcherPtr, OnReceive);
        // 使观察者开始监听订阅的事件。
        OH_HiAppEvent_AddWatcher(onReceiverWatcherPtr);
    } else {
        OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent: Watcher already exists %{public}s", event_name_tag.c_str());
    }
    napi_status status;
    napi_value result;
    status = napi_create_string_utf8(env, event_name_tag.c_str(), NAPI_AUTO_LENGTH, &result);
    if (status != napi_ok) {
        // 处理错误，例如返回undefined
        napi_value undefined;
        napi_get_undefined(env, &undefined);
        return undefined;
    }
    return result;
}

// 移除OnReceiver类型观察者
// 可参考官网：https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/hiappevent-watcher-app-events-ndk
static napi_value removeOnReceiverWatcher(napi_env env, napi_callback_info info) {
    // 获得 event name
    std::string event_name = getAppEventFromEnv(env, info);
    std::string event_name_tag = "Native-OnReceiver-Watcher-" + event_name;
    if (_onReceiverWatcherMap.count(event_name_tag) != 0) {
        HiAppEvent_Watcher *onReceiverWatcherPtr = _onReceiverWatcherMap.at(event_name_tag);
        int ret = OH_HiAppEvent_RemoveWatcher(onReceiverWatcherPtr);
        if (ret != 0) {
            OH_LOG_ERROR(LogType::LOG_APP, "HiAppEvent: Failed to remove watcher %{public}s", event_name_tag.c_str());
        } else {
            OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent: Remove watcher %{public}s successfully", event_name_tag.c_str());
        }
    } else {
        OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent: Watcher %{public}s does not exists.", event_name_tag.c_str());
    }
    return {};
}

// 订阅应用事件，采用OnTrigger类型观察者的订阅方式
static napi_value addOnTriggerWatcher(napi_env env, napi_callback_info info) {
    // 获得 event name
    std::string event_name = getAppEventFromEnv(env, info);
    std::string event_name_tag = "Native-OnTrigger-Watcher-" + event_name;
    OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent: add watch event name = %{public}s", event_name.c_str());
    // 开发者自定义观察者名称，系统根据不同的名称来识别不同的观察者。
    HiAppEvent_Watcher *onTriggerWatcherPtr = nullptr;
    if (_onTriggerWatcherMap.count(event_name_tag) == 0) {
        onTriggerWatcherPtr = OH_HiAppEvent_CreateWatcher(event_name_tag.c_str());
        onTriggerWatcherPtr = OH_HiAppEvent_CreateWatcher(event_name_tag.c_str());
        if (onTriggerWatcherPtr == nullptr) {
            OH_LOG_ERROR(LogType::LOG_APP, "HiAppEvent: Failed to add on-trigger watcher %{public}s.", event_name_tag.c_str());
            napi_value undefined;
            napi_get_undefined(env, &undefined);
            return undefined;
        }
        _onTriggerWatcherMap[event_name_tag] = onTriggerWatcherPtr;
        // 设置订阅的事件为 APP_CRASH
        const char *names[] = { event_name.c_str() };
        // 开发者订阅感兴趣的事件，此处订阅了系统事件。
        OH_HiAppEvent_SetAppEventFilter(onTriggerWatcherPtr, DOMAIN_OS, 0, names, 1);
        // 开发者设置已实现的回调函数，需OH_HiAppEvent_SetTriggerCondition设置的条件满足方可触发。
        OH_HiAppEvent_SetWatcherOnTrigger(onTriggerWatcherPtr, onTriggerCallback);
        // 设置订阅触发回调的条件：当设置新增事件打点数量为1时，触发onTrigger回调。
        OH_HiAppEvent_SetTriggerCondition(onTriggerWatcherPtr, 1, 0, 0);
        // 启动观察者以监听订阅的事件。
        OH_HiAppEvent_AddWatcher(onTriggerWatcherPtr);
    } else {
        OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent: Watcher already exists %{public}s.", event_name_tag.c_str());
    }
    napi_status status;
    napi_value result;
    status = napi_create_string_utf8(env, event_name_tag.c_str(), NAPI_AUTO_LENGTH, &result);
    if (status != napi_ok) {
        // 处理错误，例如返回undefined
        napi_value undefined;
        napi_get_undefined(env, &undefined);
        return undefined;
    }
    return result;
}

// 移除OnTrigger类型观察者
static napi_value removeOnTriggerWatcher(napi_env env, napi_callback_info info) {
    // 获得 event name
    std::string event_name = getAppEventFromEnv(env, info);
    std::string event_name_tag = "Native-OnTrigger-Watcher-" + event_name;
    if (_onTriggerWatcherMap.count(event_name_tag) != 0) {
        HiAppEvent_Watcher *onTriggerWatcherPtr = _onTriggerWatcherMap.at(event_name_tag);
        int ret = OH_HiAppEvent_RemoveWatcher(onTriggerWatcherPtr);
        if (ret != 0) {
            OH_LOG_ERROR(LogType::LOG_APP, "HiAppEvent: Failed to remove watcher %{public}s", event_name_tag.c_str());
        } else {
            OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent: Remove watcher %{public}s successfully", event_name_tag.c_str());
        }
    } else {
        OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent: Watcher %{public}s does not exists.", event_name_tag.c_str());
    }
    return {};
}

// 可参考官网：
static napi_value writeAppEventExample(napi_env env, napi_callback_info info) {
    // 向应用事件写入 button click 事件信息
    auto params = OH_HiAppEvent_CreateParamList();
    OH_HiAppEvent_AddInt64Param(params, "click_time", time(nullptr));
    OH_HiAppEvent_Write("button", "click", EventType::BEHAVIOR, params);
    OH_HiAppEvent_DestroyParamList(params);
    return {};
}
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        NAPI_BINDER("crash_npe", crash_npe),
        NAPI_BINDER("crash_runtime", crash_runtime),
        
        NAPI_BINDER("add_on_receiver_watcher", addOnReceiverWatcher),
        NAPI_BINDER("remove_on_receiver_watcher", removeOnReceiverWatcher),
        NAPI_BINDER("add_on_trigger_watcher", addOnTriggerWatcher),
        NAPI_BINDER("remove_on_trigger_watcher", removeOnTriggerWatcher),
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&demoModule);
}
